package goTeam.up.cs301.go;

import java.util.Arrays;

import goTeam.up.cs301.game.infoMsg.GameState;

/**
 * Contains the state of a Go game. Sent by the game when a player wants to
 * enquire about the state of the game. (E.g., to display it, or to help figure
 * out its next move.)
 * 
 * @author Steven R. Vegdahl
 * @author Casey Sigelmann
 * @author Ryan Gasik
 * @version December 2013
 */
public class GoState extends GameState {

	/**
	 * Satisfy serializable interface
	 */
	private static final long serialVersionUID = 5237979854063936232L;

	// /////////////////////////////////////////////////
	// ************** instance variables ************
	// /////////////////////////////////////////////////

	// the NUM_LINESxNUM_LINES array of char that represents the stones on the
	// board
	private char[][] board;

	// the char used to identify a stone in peril
	private final char inPeril = 'p';

	// an int that tells whose move it is
	private int playerToMove;

	// We need to know if the last move was a pass
	private boolean wasPass;
	
	// We need to know if the game is over
	protected boolean gameOver;

	/**
	 * @return the wasPass
	 */
	public boolean wasLastMoveAPass() {
		return wasPass;
	}

	/**
	 * @param wasPass
	 *            the wasPass to set
	 */
	public void setLastMovePassState(boolean lastMovePassState) {
		this.wasPass = lastMovePassState;
	}

	/**
	 * Constructor for objects of class GoState
	 */
	public GoState() {
		// initialize the state to be a brand new game
		board = new char[GoHumanPlayer1.NUM_LINES][GoHumanPlayer1.NUM_LINES];
		for (int i = 0; i < GoHumanPlayer1.NUM_LINES; ++i) {
			for (int j = 0; j < GoHumanPlayer1.NUM_LINES; ++j) {
				board[i][j] = ' ';
			}
		}

		// make it player 0's move
		playerToMove = 0;

		// The game doesn't start with a previous pass
		wasPass = false;
	}// constructor

	/**
	 * Copy constructor for class GoState
	 * 
	 * @param original
	 *            the GoState object that we want to clone
	 */
	public GoState(GoState original) {
		// create a new array, and copy the values from the original
		board = new char[GoHumanPlayer1.NUM_LINES][GoHumanPlayer1.NUM_LINES];
		for (int i = 0; i < GoHumanPlayer1.NUM_LINES; ++i) {
			for (int j = 0; j < GoHumanPlayer1.NUM_LINES; ++j) {
				board[i][j] = original.board[i][j];
			}
		}

		// copy the player-to-move information
		playerToMove = original.playerToMove;

		// copy the pass information
		wasPass = original.wasPass;
	}

	/**
	 * Find out which piece is on an intersection
	 * 
	 * @param row
	 *            the row being queried
	 * @param col
	 *            the column being queried
	 * @return the piece at the given intersection; ' ' if no piece there; '?'
	 *         if it is an illegal intersection
	 */
	public char getPiece(int row, int col) {
		// if we're out of bounds or anything, return '?';
		if (board == null || row < 0 || col < 0)
			return '?';
		if (row >= board.length || col >= board[row].length)
			return '?';

		// return the character that is in the proper position
		return board[row][col];
	}

	/**
	 * Sets a piece on an intersection
	 * 
	 * @param row
	 *            the row being queried
	 * @param col
	 *            the column being queried
	 * @param piece
	 *            the piece to place
	 */
	public void setPiece(int row, int col, char piece) {
		// if we're out of bounds or anything, return;
		if (board == null || row < 0 || col < 0)
			return;
		if (row >= board.length || col >= board[row].length)
			return;

		// return the character that is in the proper position
		board[row][col] = piece;
	}

	/**
	 * Gets the score of one player
	 * 
	 * @param piece
	 * 			the type of piece to be counted
	 * @return the number of specified piece present
	 */
	public int getScore(char piece) {

		//find opposite piece
		char otherPiece;
		if (piece == 'b') {
			otherPiece = 'w';
		}
		else {
			otherPiece = 'b';
		}

		//number given piece on the board
		int result = 0;
		for(int i=0; i<board.length; ++i)
		{
			for(int j=0; j<board[i].length; ++j)
			{
				if (board[i][j] == piece)
				{
					++result;
				}
			}
		}
		
		// Take care of the early-game, when no containment is possible
		if (result < 2) return result;

		//spaces within territory
		markPeril(' ');
		removePeril(' ', otherPiece);
		for(int i=0; i<board.length; ++i) {
			for(int j=0; j<board[i].length; ++j) {
				if(board[i][j] == inPeril) {
					++result;
				}
			}
		}
		for(int i=0; i<board.length; ++i) {
			for(int j=0; j<board[i].length; ++j) {
				if(board[i][j] == inPeril) {
					board[i][j] = ' ';
				}
			}
		}

		return result;
	}

	/**
	 * Tells whose move it is.
	 * 
	 * @return the index (0 or 1) of the player whose move it is.
	 */
	public int getWhoseMove() {
		return playerToMove;
	}

	/**
	 * set whose move it is
	 * 
	 * @param id
	 *            the player we want to set as to whose move it is
	 */
	public void setWhoseMove(int id) {
		playerToMove = id;
	}

	/**
	 * Tells whether a piece on the board is safe from capture after pieces in
	 * peril are identified.
	 * 
	 * @param row
	 *            the row being queried
	 * @param col
	 *            the column being queried
	 * @param color
	 *            the type of stone in question represented as a char
	 * @param safety
	 * 			  the secondary state of an intersection that makes stones okay
	 */
	private boolean isOkay(int row, int col, char color, char safety) {
		// if next to a space or a safe stone, this stone is safe
		if (row - 1 >= 0
				&& (board[row - 1][col] == color || board[row - 1][col] == safety)) {
			return true;
		}
		if (row + 1 < board.length
				&& (board[row + 1][col] == color || board[row + 1][col] == safety)) {
			return true;
		}
		if (col - 1 >= 0
				&& (board[row][col - 1] == color || board[row][col - 1] == safety)) {
			return true;
		}
		if (col + 1 < board.length
				&& (board[row][col + 1] == color || board[row][col + 1] == safety)) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * marks all stones of specified color as in peril
	 * 
	 * @param color
	 * 			  the type of stone in question
	 */
	private void markPeril(char color) {
		// sets all stones of given color on the board to be "in peril" so they
		// will be checked
		for (int i = 0; i < board.length; ++i) {
			for (int j = 0; j < board[i].length; ++j) {
				if (board[i][j] == color) {
					board[i][j] = inPeril;
				}
			}
		}	}

	/**
	 * marks stones that are labeled in peril and shouldn't be back to their original state
	 * 
	 * @param color
	 * 			  the original color / the color to be reverted back to
	 * @param safety
	 * 			  a secondary state of an intersection that makes stones safe
	 */

	private void removePeril(char color, char safety) {
		// If a stone has a liberty or is next to a safe stone, make it safe
		// Repeat this until no changes are made
		boolean isChange = true;
		while (isChange) {
			isChange = false;
			for (int i = 0; i < board.length; ++i) {
				for (int j = 0; j < board[i].length; ++j) {
					if (board[i][j] == inPeril && isOkay(i, j, color, safety)) {
						board[i][j] = color;
						isChange = true;
					}
				}
			}
		}
	}
	/**
	 * Tells whether the indicated space is free.
	 * @param row Row
	 * @param col Column 
	 * @return Whether the space is free
	 */
	public boolean isFree(int row, int col)
	{
		if ((row < 0 || row >= board.length) || (col < 0 || col >= board.length))
		{
			return false;
		}
		return (board[row][col] == ' ');
	}
	/**
	 * Tells whether the indicated empty space has liberties.
	 * @param row Row
	 * @param col Column
	 * @param color Our Color
	 * @return Whether or not the space has any liberties.
	 */
	public boolean emptySpaceHasLiberties(int row, int col, char color)
	{
		markPeril(color);
		removePeril(color, ' ');
		//Select the surrounding coordinates;
		int[] y = {row-1, row, row, row+1}; 
		int[] x = {col, col+1, col-1, col};
		boolean result = false;
		try {

			for (int i = 0; i < x.length; i++)
			{
					char space = getPiece(y[i], x[i]);
					if (space == '?')
						continue;
					if (space != inPeril && !(space == 'b' ))
					{
						result = true;
					}
			}
			// set in peril stones back to original color to avoid possibly
			// trying to draw inPeril stones
			for (int i = 0; i < board.length; ++i) {
				for (int j = 0; j < board[i].length; ++j) {
					if (board[i][j] == inPeril) {
						board[i][j] = color;
					}
				}
			}
		} catch (ArrayIndexOutOfBoundsException ex) {
			return false;
		}

		return result;
	}
	/**
	 * Tells whether the indicated piece has liberties.
	 * @param row The row of the piece.
	 * @param col The column of the piece.
	 * @param color The color of the player to be checked.
	 * @return Whether that piece has any liberties.
	 */
	public boolean hasLiberties(int row, int col, char color) {
		// identify which stones are in peril
		markPeril(color);
		removePeril(color, ' ');

		boolean result = false;
		try {

			// return true if given stone is in peril
			if (board[row][col] != inPeril) {
				result = true;
			}

			// set in peril stones back to original color to avoid possibly
			// trying to draw inPeril stones
			for (int i = 0; i < board.length; ++i) {
				for (int j = 0; j < board[i].length; ++j) {
					if (board[i][j] == inPeril) {
						board[i][j] = color;
					}
				}
			}
		} catch (ArrayIndexOutOfBoundsException ex) {
			return false;
		}

		return result;
	}

	/**
	 * Iterates through the board and removes pieces without liberties.
	 * 
	 * @param color
	 *            the type of stone in question
	 */
	public void removeCapturedStones(char color) {

		// identify which stones are in peril
		markPeril(color);
		removePeril(color, ' ');

		// After all safe stones are indicated, remove stones still in peril
		for (int i = 0; i < board.length; ++i) {
			for (int j = 0; j < board[i].length; ++j) {
				if (board[i][j] == inPeril) {
					board[i][j] = ' ';
				}
			}
		}

	}

	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 * Checks to see if the board in this object is equivalent to another
	 * 
	 * If things start going belly-up with hashes, override hashCode as well,
	 * since overriding this method only violates the guarantee that equal
	 * objects hash to the same key
	 */
	@Override
	public boolean equals(Object obj){
		if (!(obj instanceof GoState)){
			return super.equals(obj);
		}
		return Arrays.deepEquals(board, ((GoState)obj).board);
	}

}
